home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Libris Britannia 4
/
science library(b).zip
/
science library(b)
/
DDJMAG
/
DDJ9105.ZIP
/
WIDGETS.ASC
< prev
Wrap
Text File
|
1991-03-15
|
17KB
|
636 lines
_MAKING SMALLTALK WITH WIDGETS_
by Kenneth E. Ayers
[LISTING ONE]
"The Appointment class definition"
Object subclass: #Appointment
instanceVariableNames:
'user date startTime endTime notifyTime text '
classVariableNames: ''
poolDictionaries: ''
"**** Appointment class methods ****"
timePrintString:aTime
"Answer a string with a representation of
aTime formatted as hh:mm AM/PM"
| amPM hours minutes minStr |
amPM := 'AM'.
hours := aTime hours.
minutes := aTime minutes.
hours = 0
ifTrue: [hours := 12. amPM := 'PM']
ifFalse:[
hours >= 12
ifTrue:[
amPM := 'PM'.
hours > 12
ifTrue:[hours := hours - 12]]].
minutes < 10
ifTrue: [minStr := '0', minutes printString]
ifFalse:[minStr := minutes printString].
^hours printString, ':', minStr, ' ', amPM.
"**** Appointment instance methods ****"
< anAppointment
"Answer true if the receiver's date
and notify time are earlier than
those of anAppointment"
^(date < anAppointment date)
or:[(date = anAppointment date)
and:[notifyTime < anAppointment notifyTime]].
<= anAppointment
"Answer true if the receiver's date
and notify time are the same or
earlier than those of anAppointment"
^(self < anAppointment) or:[self = anAppointment].
= anAppointment
"Answer true if the receiver's date
and notify time are the same as
those of anAppointment"
^(date = anAppointment date)
and:[notifyTime = anAppointment notifyTime].
> anAppointment
"Answer true if the receiver's date
anf notify time are later than
those of anAppointment"
^(self <= anAppointment) not.
>= anAppointment
"Answer true if the receiver's date
and notify time are the same or
later than those of anAppointment"
^(self < anAppointment) not.
checkTime:timeNow date:dateToday
"Answer true if it is time to notify
the user of his or her appointment"
^(date < dateToday)
or:[(date = dateToday)
and:[notifyTime < timeNow]].
date
"Answer a Date, the appointment's date"
^date.
date:aDate
"Set the appointment's date to aDate"
date := aDate.
endTime
"Answer a Time, the appointment's end time"
^endTime.
endTime:aTime
"Set the appointment's end time to aTime"
endTime := aTime.
info
"Answer a String with the information on
this appointment"
^OrderedCollection new
add:user, ' has an appointment';
add:'on ', date formPrint,
' at ', (self timePrintString:startTime);
add:String new;
addAll:text;
yourself.
notifyTime
"Answer a Time, the time at which the
user is to be notified"
^notifyTime.
notifyTime:aTime
"Set the time at which the user is to
be notified to aTime"
notifyTime := aTime.
printOn:aStream
"Add a representation of the receiver
to aStream"
aStream
nextPutAll:date formPrint;
nextPutAll:' @ ';
nextPutAll:(self timePrintString:startTime);
nextPutAll:' - ';
nextPutAll:(text at:1).
startTime
"Answer a Time, the appointment's start time"
^startTime.
startTime:aTime
"Set the appointment's start time to aTime"
startTime := aTime.
text
"Answer an Array containing the lines of text
that describe the appointment"
^text.
text:aTextArray
"Set aTextArray as the lines of text
that describe the appointment"
text := aTextArray.
timePrintString:aTime
"Answer a string with a representation of
aTime formatted as hh:mm AM/PM"
^self class timePrintString:aTime.
user
"Answer the user for whom the appointment
was created"
^user.
user:userName
"Set the user for whom the appointment
was created to userName"
user := userName.
[LISTING TWO]
"The AppointmentBrowser class definition"
ApplicationWindow subclass: #AppointmentBrowser
instanceVariableNames:
'user appointments appointmentList
dateEditor textEditor notifyTimeEditor
endTimeEditor startTimeEditor '
classVariableNames: ''
poolDictionaries: ''
"**** AppointmentBrowser class methods ****"
open
"Prompt for a user name and then open
an AppointmentBrowser for that user"
| userName |
Cursor offset:Display boundingBox center.
userName := PromptDialog
prompt:'Enter user name'.
userName isNil ifTrue:[^nil].
^self openOn:userName.
openOn:aUserName
"Open an AppointmentBrowser for aUserName"
^super new
openWithInitializeMethod:#initializeUser:
arguments:(Array with:aUserName).
"**** AppointmentBrowser instance methods ****"
addAppointment
"The user as pushed the 'ADD' button so
we need to construct a new appointment
record and add it to the user's list"
| appointment index |
appointment := Appointment new
user:user;
date:dateEditor date;
startTime:startTimeEditor time;
endTime:endTimeEditor time;
notifyTime:notifyTimeEditor time;
text:textEditor stringList deepCopy;
yourself.
appointments add:appointment.
index := appointments indexOf:appointment.
self updateAppointmentList.
appointmentList selectItem:index.
changeAppointment
"The user has pushed the 'CHANGE' button so
we need to remove the currently selected
appointment and then add a new on with the
current values"
appointmentList disableDrawing.
self removeAppointment.
appointmentList enableDrawing.
self addAppointment.
createWindow
"This method was generated by the
Widgets/V 286 Interface Editor"
^TitledWindow new
yourself;
title: 'Appointment Browser';
closable: true;
iconizable: true;
size: 415 @ 322;
addWidget: (
appointmentList := ListBox new
yourself;
nameForInstVar: 'appointmentList';
on: #select send: #selectAppointment:;
title: 'Appointments';
size: 390 @ 133
) position: 12 @ 9;
addWidget: (
dateEditor := DateWidget new
yourself;
nameForInstVar: 'dateEditor';
title: 'DATE:';
date: (
Date newDay: 16
month: #December
year: 1990
);
size: 114 @ 18
) position: 47 @ 156;
addWidget: (
TitledPane new
yourself;
title: 'WHAT FOR?';
size: 233 @ 116;
addWidget: (
textEditor := TextEditWidget new
yourself;
nameForInstVar: 'textEditor';
verticalScrollBar: true;
horizontalScrollBar: false;
size: 219 @ 90
) framer: (
FramingParameters new
xCentered;
yCentered
)
) position: 170 @ 151;
addWidget: (
notifyTimeEditor := TimeWidget new
yourself;
nameForInstVar: 'notifyTimeEditor';
title: 'NOTIFY AT:';
time: (
Time new seconds: 74673
);
size: 144 @ 18
) position: 16 @ 269;
addWidget: (
TitledButton new
yourself;
on: #release send: #changeAppointment;
title: 'CHANGE';
size: 77 @ 19
) position: 326 @ 269;
addWidget: (
endTimeEditor := TimeWidget new
yourself;
nameForInstVar: 'endTimeEditor';
title: 'END TIME:';
time: (
Time new seconds: 74673
);
size: 136 @ 18
) position: 24 @ 230;
addWidget: (
startTimeEditor := TimeWidget new
yourself;
nameForInstVar: 'startTimeEditor';
on: #valueChanged send: #startTimeChanged:;
title: 'START TIME:';
time: (
Time new seconds: 74613
);
size: 148 @ 18
) position: 12 @ 192;
addWidget: (
TitledButton new
yourself;
on: #release send: #addAppointment;
title: 'ADD';
size: 77 @ 19
) position: 170 @ 269;
addWidget: (
TitledButton new
yourself;
on: #release send: #removeAppointment;
title: 'REMOVE';
size: 77 @ 19
) position: 248 @ 269.
initializeUser:argArray
"Initialize an AppointmentBrowser for the
user whose name is given in argArray"
user := argArray.
(Appointments includesKey:user)
ifFalse:[
Appointments
at:user
put:(SortedCollection
sortBlock:[:a :b| a < b])].
appointments := Appointments at:user.
self window
on:#activated send:#updateAppointmentList;
title:'Appointments for ', user.
dateEditor date:Date today.
startTimeEditor time:(Time fromSeconds:32400). "9:00 AM"
self
startTimeChanged:startTimeEditor time;
updateAppointmentList.
removeAppointment
"The user has pushed the 'REMOVE' button so
we need to remove the selected appointment
from the user's list"
| index appointment |
appointments size = 0 ifTrue:[^self].
index := appointmentList selectionIndex.
appointment := appointments at:index.
appointments remove:appointment ifAbsent:[].
self updateAppointmentList.
index > appointments size
ifTrue:[index := appointments size].
appointmentList selectItem:index.
selectAppointment:aString
"The user has selected an appointment so
we need to fill-in all of the appropriate
field editors"
| appointment |
appointment :=
appointments
at:appointmentList selectionIndex.
dateEditor date:appointment date.
startTimeEditor time:appointment startTime.
endTimeEditor time:appointment endTime.
notifyTimeEditor time:appointment notifyTime.
textEditor stringList:appointment text.
startTimeChanged:aString
"The user has changed the start time
so we need to supply rerasonable
defaults for the end time and the
notify time"
| aTime |
aTime := startTimeEditor time.
"Assume appointment is one hour long"
endTimeEditor
time:(aTime addTime:(Time fromSeconds:3600)).
"Assume notify 5-minutes before"
notifyTimeEditor
time:(aTime subtractTime:(Time fromSeconds:300)).
updateAppointmentList
"Update the list of appointments"
| size list |
(size := appointments size) = 0 ifTrue:[^self].
list := Array new:size.
1 to:size do:[:index|
list
at:index
put:(appointments at:index) printString].
appointmentList stringList:list.
user
"Answer the name of the user for which
this browser was opened"
^user.
windowLocation
"Make the window centered on the screen"
^#center.
[LISTING THREE]
"The AppointmentNotifier class definition"
ApplicationWindow subclass: #AppointmentNotifier
instanceVariableNames:
'active running minute text appointment '
classVariableNames: ''
poolDictionaries: ''
"**** AppointmentNotifier instance methods ****"
acknowledge
"The user has pushed th 'OK' button, so we
need to remove the current appointment
from the user's list"
| user list |
appointment isNil ifTrue:[^self].
user := appointment user.
(list := Appointments
at:user
ifAbsent:[nil]) isNil
ifFalse:[
list
remove:appointment
ifAbsent:[]].
text
stringList:#();
display.
appointment := nil.
minute := nil.
activateFor:anAppointment
"Display the details of anAppointment and
bring this window to the top"
appointment isNil
ifTrue:[
"Previous appointment
has been dismissed"
text
disableDrawing;
stringList:anAppointment info;
enableDrawing.
appointment := anAppointment].
Terminal bell; bell.
Cursor offset:window origin.
ScreenManager activateWindow:self window.
clockEvent
"A clock tick has been received so we have
to determine if the minute has rolled over
and, if so, are any appointments ready"
| now thisMinute today list appointment |
now := Time now.
(thisMinute := now minutes) = minute
ifTrue:[^self].
minute := thisMinute.
today := Date today.
Appointments associationsDo:[:anEntry|
(list := anEntry value) size > 0
ifTrue:[
((appointment := list first)
checkTime:now date:today)
ifTrue:[self activateFor:appointment]]].
closeWindow
"Before closing this window, terminate the
process that's monitoring clock events"
active := false.
"Wait for the process to terminate"
[running] whileTrue:[Processor yield].
super closeWindow.
createWindow
"This method was generated by the
Widgets/V 286 Interface Editor"
^TitledWindow new
yourself;
title: 'Appointment Notifier';
closable: true;
size: 183 @ 155;
addWidget: (
text := TextEditWidget new
yourself;
nameForInstVar: 'text';
verticalScrollBar: true;
horizontalScrollBar: false;
size: 169 @ 91
) position: 5 @ 6;
addWidget: (
TitledButton new
yourself;
on: #release send: #acknowledge;
title: 'OK';
default: true;
size: 57 @ 23
) framer: (
FramingParameters new
xCentered;
originY: 103 relativeTo: #origin
).
initialize
"Initialize a new AppointmentNotifier"
active := false.
running := false.
minute := Time now minutes.
[self run] forkAt:Processor highUserPriority.
run
"Run the process that handles clock events"
| timerSemaphore |
(timerSemaphore := Smalltalk
at:#TimerSemaphore
ifAbsent:[nil])
isNil
ifTrue:[
timerSemaphore := Semaphore new.
Smalltalk
at:#TimerSemaphore
put:timerSemaphore].
active := true.
running := true.
[active]
whileTrue:[
timerSemaphore wait.
self clockEvent].
Smalltalk removeKey:#TimerSemaphore.
running := false.
windowLocation
"Make the window centered on the screen"
^#center.
[LISTING FOUR]
"Modifications to the Process class methods"
timerInterrupt
"Implement the timer interrupt."
| timerSemaphore |
PendingEvents add: (Message new
selector: #clockEvent:;
arguments: (Array with: 1)).
KeyboardSemaphore signal.
"**********************************************
Added by Ken Ayers to support the
Appointment Manager Application"
timerSemaphore := Smalltalk at:#TimerSemaphore
ifAbsent:[nil].
timerSemaphore notNil
ifTrue:[timerSemaphore signal].
"**********************************************"
self enableInterrupts: true.
[LISTING FIVE]
"Corrections to TimeWidget methods"
"**** TimeWidget instance methods ****"
time
| hours |
hours := self hours.
(self meridianEditor value = 'PM')
ifTrue:[
hours < 12 ifTrue:[hours := hours + 12]]
ifFalse:[
hours = 12 ifTrue:[ hours := 0]].
^(Time fromSeconds:0)
hours:hours;
minutes:self minutes.
time: newTime
| hours |
self time = newTime ifTrue: [^self].
hours := newTime hours.
(hours >= 12)
ifTrue: [
self meridianEditor value: 'PM'.
hours > 12 ifTrue:[hours := hours - 12]]
ifFalse: [
self meridianEditor value: 'AM'.
hours = 0 ifTrue:[hours := 12]].
self hourEditor value: hours.
self minuteEditor value: newTime minutes.